(function (root, factory, undef) {
    if (typeof exports === 'object') {
        // CommonJS
        module.exports = exports = factory(require('./core'), require('./cipher-core'));
    } else {
        if (typeof define === 'function' && define.amd) {
            // AMD
            define(['./core', './cipher-core'], factory);
        } else {
            // Global (browser)
            factory(root.CryptoJS);
        }
    }
})(this, function (CryptoJS) {
    /** @preserve
     * Counter block mode compatible with  Dr Brian Gladman fileenc.c
     * derived from CryptoJS.mode.CTR
     * Jan Hruby jhruby.web@gmail.com
     */
    CryptoJS.mode.CTRGladman = (function () {
        var CTRGladman = CryptoJS.lib.BlockCipherMode.extend();

        function incWord(word) {
            if (((word >> 24) & 255) === 255) {
                //overflow
                var b1 = (word >> 16) & 255;
                var b2 = (word >> 8) & 255;
                var b3 = word & 255;

                if (b1 === 255) {
                    // overflow b1
                    b1 = 0;

                    if (b2 === 255) {
                        b2 = 0;

                        if (b3 === 255) {
                            b3 = 0;
                        } else {
                            ++b3;
                        }
                    } else {
                        ++b2;
                    }
                } else {
                    ++b1;
                }

                word = 0;
                word += b1 << 16;
                word += b2 << 8;
                word += b3;
            } else {
                word += 1 << 24;
            }

            return word;
        }

        function incCounter(counter) {
            if ((counter[0] = incWord(counter[0])) === 0) {
                // encr_data in fileenc.c from  Dr Brian Gladman's counts only with DWORD j < 8
                counter[1] = incWord(counter[1]);
            }

            return counter;
        }

        var Encryptor = (CTRGladman.Encryptor = CTRGladman.extend({
            processBlock: function (words, offset) {
                // Shortcuts
                var cipher = this._cipher;
                var blockSize = cipher.blockSize;
                var iv = this._iv;
                var counter = this._counter; // Generate keystream

                if (iv) {
                    counter = this._counter = iv.slice(0); // Remove IV for subsequent blocks

                    this._iv = undefined;
                }

                incCounter(counter);
                var keystream = counter.slice(0);
                cipher.encryptBlock(keystream, 0); // Encrypt

                for (var i = 0; i < blockSize; i++) {
                    words[offset + i] ^= keystream[i];
                }
            }
        }));
        CTRGladman.Decryptor = Encryptor;
        return CTRGladman;
    })();

    return CryptoJS.mode.CTRGladman;
});
